import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
import env from "/@/base/env";
import { message } from "ant-design-vue";
import { HttpStatusEnum, ResultCodeEnum, ContentTypeEnum } from "./httpEnum";
import getCodeMessage from "./getCodeMessage";
import { useI18n } from "/@/locales/useI18n";
import authCache from "/@/utils/cache/authCache";
import router from "/@/router";
import { store } from "/@/store";
import { RequestMethodEnum } from "/@/api/req/httpEnum";

export const AuthenticationScheme = "Bearer";
const instance = axios.create();

Object.assign(instance.defaults, {
  baseURL: env.baseUrl,
  headers: {
    "Content-Type": ContentTypeEnum.JSON,
  },
});
instance.interceptors.request.use(
  function (config) {
    const key = config.method === "get" ? "params" : "data";
    let target = config[key];
    if (!target) {
      target = config[key] = {};
    }
    console.log(config.url, "发送参数", target);
    return config;
  },
  function (error) {
    return Promise.reject(error);
  }
);
instance.interceptors.response.use(
  function (response) {
    const { data, config } = response;
    console.log(config.url, "接收数据", data);
    return response;
  },
  function (error) {
    return Promise.reject(error);
  }
);
export interface ResponseData<T> {
  code: number;
  data: T;
  msg?: string | null;
  errorMsg?: string | null;
  extra?: any;
  isSuccess?: boolean;
  path?: string | null;
  timestamp?: number;
  token?: string;
}
export interface RequsetOptons {
  //注意当采用默认的失败或异常处理时 最后会resolve(null)
  //当result code不为0或200时，是否采用默认的失败处理
  useDefaultFailHandle?: boolean;
  //当httpstatus不为200时，是否采用默认的异常处理
  useDefaultErrHandle?: boolean;
  //是否需要token
  withToken?: boolean;
  //是否强制使用mock url
  mock?: boolean;
  //是否使用全局loading
  useGlobalLoading?: boolean;
}
interface ReqError {
  response: AxiosResponse;
  request: any;
}
function dealErrorResponse(response: AxiosResponse<ResponseData<any>>): ResponseData<any> {
  if (response && response.data) {
    const codeMsg = getCodeMessage(response.data.code);
    if (codeMsg) {
      response.data.msg = codeMsg;
    }
  }
  return (
    response.data || {
      code: response.status,
      msg: response.statusText,
    }
  );
}
function hideLoading() {
  store.dispatch("toggleLoading", {
    loading: false,
  });
}
function doReq<T>(config: AxiosRequestConfig, options?: RequsetOptons): Promise<ResponseData<T> | null | T> {
  options = Object.assign(
    {
      useDefaultFailHandle: true,
      useDefaultErrHandle: true,
      withToken: true,
      mock: false,
      useGlobalLoading: true,
    },
    options || {}
  );
  if (options.withToken) {
    if (!config.headers) {
      config.headers = {};
    }
    const token = authCache.getToken();
    if (token) {
      config.headers.Authorization = `${AuthenticationScheme} ${token}`;
    } else {
      router.push("/login");
      return Promise.reject(null);
    }
  }
  if (options.mock) {
    config.baseURL = env.mockBaseUrl;
  }
  if (options.useGlobalLoading) {
    store.dispatch("toggleLoading", {
      loading: true,
    });
  }
  return new Promise((resolve, reject) => {
    instance
      .request(config)
      .then((resp: AxiosResponse<ResponseData<T>>) => {
        if (resp.status === HttpStatusEnum.SUCCESS) {
          const data = resp.data;
          if (data) {
            if (data.code === ResultCodeEnum.SUCCESS || data.code === ResultCodeEnum.SUCCESS200) {
              resolve(data.data ? data.data : data);
            } else if (data.code === ResultCodeEnum.UNAUTHORIZED || data.code === ResultCodeEnum.JWT_NOT_LOGIN) {
              //token过期或未登录
              router.push("/login");
              resolve(null);
              if (data.code === ResultCodeEnum.JWT_NOT_LOGIN) {
                message.error(getCodeMessage(ResultCodeEnum.JWT_NOT_LOGIN) as string);
              }
            } else {
              const codeMsg = getCodeMessage(data.code);
              if (codeMsg) {
                data.msg = codeMsg;
              }
              if (options?.useDefaultFailHandle) {
                const { t } = useI18n();
                message.error(data.msg || t("common.loadError"));
                resolve(null);
              } else {
                reject(data);
              }
            }
            hideLoading();
            return;
          }
        }
        reject({
          code: resp.status,
          msg: resp.statusText,
        });
        hideLoading();
      })
      .catch((err: ReqError) => {
        const { t } = useI18n();
        const { response } = err;
        const data = response
          ? dealErrorResponse(response)
          : {
              msg: null,
              code: null,
            };
        if (options?.useDefaultErrHandle) {
          //默认异常处理
          message.error(data.msg || t("common.loadError"));
          resolve(null);
          if (data.code === ResultCodeEnum.JWT_NOT_LOGIN || data.code === ResultCodeEnum.JWT_TOKEN_EXPIRED) {
            router.push("/login");
          }
        } else {
          //自定义异常处理
          reject(data);
        }
        hideLoading();
      });
  });
}
interface Req {
  [key: string]: <T>(config: AxiosRequestConfig, options?: RequsetOptons) => Promise<ResponseData<T> | null | T>;
}
const req: Req = {
  get<T>(config: AxiosRequestConfig, options?: RequsetOptons) {
    config.method = RequestMethodEnum.GET;
    return doReq<T>(config, options);
  },
  post<T>(config: AxiosRequestConfig, options?: RequsetOptons) {
    config.method = RequestMethodEnum.POST;
    return doReq<T>(config, options);
  },
  delete<T>(config: AxiosRequestConfig, options?: RequsetOptons) {
    config.method = RequestMethodEnum.DELETE;
    return doReq<T>(config, options);
  },
  put<T>(config: AxiosRequestConfig, options?: RequsetOptons) {
    config.method = RequestMethodEnum.PUT;
    return doReq<T>(config, options);
  },
  upload<T>(config: AxiosRequestConfig, options?: RequsetOptons) {
    config.method = RequestMethodEnum.POST;
    if (!config.headers) {
      config.headers = {};
    }
    config.headers["Content-Type"] = ContentTypeEnum.FORM_DATA;
    const data = config.data;
    const uploadData = new FormData();
    for (const i in data) {
      uploadData.append(i, data[i]);
    }
    config.data = uploadData;
    return doReq<T>(config, options);
  },
};
export default req;
